﻿//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2020 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using UnityEditor;
using UnityEngine;
using Core;

namespace UnityGameFramework.Editor.ResourceTools
{
    public sealed partial class ResourceBuilderController : IResourceBuilderControllerInfo
    {
        public enum ESdkChannel
        {
            bilibili,
            blLy,
            quickgame
        }

        private const string RemoteVersionListFileName = "GameFrameworkVersion.dat";
        private const string LocalVersionListFileName = "GameFrameworkList.dat";
        private const string DefaultExtension = "dat";
        private const string NoneOptionName = "<None>";
        private static readonly int AssetsStringLength = "Assets".Length;

        private readonly string m_ConfigurationPath;
        private readonly ResourceBuilderCollection m_ResourceCollection;
        private readonly ResourceAnalyzerController m_ResourceAnalyzerController;
        private readonly ABRedundancyChecker m_ABRedundancyChecker;
        private readonly ABDistributionChecker m_ABDistributionChecker;

        private readonly SortedDictionary<string, ResourceData> m_ResourceDatas;

        //private readonly Dictionary<string, FileSystem> m_OutputPackageFileSystems;
        //private readonly Dictionary<string, FileSystem> m_OutputPackedFileSystems;
        //private readonly Dictionary<string, FileSystem> m_OutputPureFileSystems;
        private readonly BuildReport m_BuildReport;
        private readonly List<string> m_BuildEventHandlerTypeNames;
        private IBuildEventHandler m_BuildEventHandler;

        public ResourceBuilderController()
        {
            m_ConfigurationPath = Type.GetConfigurationPath<ResourceBuilderConfigPathAttribute>() ??
                                  Utils.File.GetRegularPath(Path.Combine(Application.dataPath,
                                      "GameFramework/Configs/ResourceBuilder.xml"));

            m_ResourceCollection = new ResourceBuilderCollection();
            //Utils.Zip.SetZipHelper(new DefaultZipHelper());

            m_ResourceCollection.OnLoadingResource += delegate(int index, int count)
            {
                OnLoadingResource?.Invoke(index, count);
            };

            m_ResourceCollection.OnLoadingAsset += delegate(int index, int count)
            {
                OnLoadingAsset?.Invoke(index, count);
            };

            m_ResourceCollection.OnLoadCompleted += delegate() { OnLoadCompleted?.Invoke(); };

            m_ResourceAnalyzerController = new ResourceAnalyzerController(m_ResourceCollection);

            m_ResourceAnalyzerController.OnAnalyzingAsset += delegate(int index, int count)
            {
                OnAnalyzingAsset?.Invoke(index, count);
            };

            m_ResourceAnalyzerController.OnAnalyzeCompleted += delegate() { OnAnalyzeCompleted?.Invoke(); };

            m_ABRedundancyChecker = new ABRedundancyChecker();
            m_ABDistributionChecker = new ABDistributionChecker();

            m_ResourceDatas = new SortedDictionary<string, ResourceData>(StringComparer.Ordinal);
            //m_OutputPackageFileSystems = new Dictionary<string, FileSystem>(StringComparer.Ordinal);
            //m_OutputPackedFileSystems = new Dictionary<string, FileSystem>(StringComparer.Ordinal);
            //m_OutputPureFileSystems = new Dictionary<string, FileSystem>(StringComparer.Ordinal);
            m_BuildReport = new BuildReport();

            m_BuildEventHandlerTypeNames = new List<string>
            {
                NoneOptionName
            };

            m_BuildEventHandlerTypeNames.AddRange(Type.GetEditorTypeNames(typeof(IBuildEventHandler)));
            m_BuildEventHandler = null;

            SelectedPlatform = Platform.Undefined;
            ZipSelected = true;
            DeterministicAssetBundleSelected = ChunkBasedCompressionSelected = true;
            UncompressedAssetBundleSelected = DisableWriteTypeTreeSelected = ForceRebuildAssetBundleSelected =
                IgnoreTypeTreeChangesSelected = AppendHashToAssetBundleNameSelected = false;
            OnlySingleBuildSelected = false;
            OutputPackageSelected = OutputFullSelected = OutputPackedSelected = true;
            BuildEventHandlerTypeName = string.Empty;
            OutputDirectory = string.Empty;
        }

        public string ProductName => PlayerSettings.productName;

        public string CompanyName => PlayerSettings.companyName;

        public string GameIdentifier => PlayerSettings.applicationIdentifier;

        public string GameFrameworkVersion => "2020.11.03";

        public string UnityVersion => Application.unityVersion;

        public string ApplicableGameVersion => Application.version;

        public int InternalGameVersion { get; set; }

        public int InternalResourceVersion { get; set; }

        public Platform SelectedPlatform { get; set; }

        public ESdkChannel SdkChannel = ESdkChannel.bilibili;

        public bool ZipSelected { get; set; }

        public bool UncompressedAssetBundleSelected { get; set; }

        public bool DisableWriteTypeTreeSelected { get; set; }

        public bool DeterministicAssetBundleSelected { get; set; }

        public bool ForceRebuildAssetBundleSelected { get; set; }
        public bool OnlySingleBuildSelected { get; set; }

        public bool IgnoreTypeTreeChangesSelected { get; set; }

        public bool AppendHashToAssetBundleNameSelected { get; set; }

        public bool ChunkBasedCompressionSelected { get; set; }

        public bool OutputPackageSelected { get; set; }

        public bool OutputFullSelected { get; set; }

        public bool OutputPackedSelected { get; set; }

        public bool OutputPureSelected { get; set; }

        public string BuildEventHandlerTypeName { get; set; }

        public string OutputDirectory { get; set; }

        public string SVNPath { get; set; }

        public bool OssProdMode { get; set; }

        public string IOSAppUrl { get; set; }

        public string AndroidAppUrl { get; set; }

        public bool IsValidOutputDirectory
        {
            get
            {
                if (string.IsNullOrEmpty(OutputDirectory))
                {
                    return false;
                }

                if (!Directory.Exists(OutputDirectory))
                {
                    return false;
                }

                return true;
            }
        }

        public string WorkingPath =>
            Utils.File.GetRegularPath(new DirectoryInfo(Utils.Text.Format("{0}/Working/", OutputDirectory)).FullName);

        public string OutputFullPath => Utils.File.GetRegularPath(new DirectoryInfo(
            Utils.Text.Format("{0}/Full/{1}_{2}/", OutputDirectory, ApplicableGameVersion.Replace('.', '_'),
                InternalResourceVersion.ToString())).FullName);

        public string OutputPackagePath => Utils.File.GetRegularPath(
            new DirectoryInfo(Utils.Text.Format("{0}/Package/{1}_{2}/", OutputDirectory,
                ApplicableGameVersion.Replace('.', '_'), InternalResourceVersion.ToString())).FullName);

        public string OutputPackedPath => Utils.File.GetRegularPath(
            new DirectoryInfo(Utils.Text.Format("{0}/Packed/{1}_{2}/", OutputDirectory,
                ApplicableGameVersion.Replace('.', '_'), InternalResourceVersion.ToString())).FullName);

        public string OutputPurePath => Utils.File.GetRegularPath(new DirectoryInfo(
            Utils.Text.Format("{0}/Pure/{1}_{2}/", OutputDirectory, ApplicableGameVersion.Replace('.', '_'),
                InternalResourceVersion.ToString())).FullName);

        public string BuildReportPath => Utils.File.GetRegularPath(new DirectoryInfo(
            Utils.Text.Format("{0}/BuildReport/{1}_{2}/", OutputDirectory, ApplicableGameVersion.Replace('.', '_'),
                InternalResourceVersion.ToString())).FullName);


        public event Action<int, int> OnLoadingResource;

        public event Action<int, int> OnLoadingAsset;

        public event Action OnLoadCompleted;

        public event Action<int, int> OnAnalyzingAsset;

        public event Action OnAnalyzeCompleted;

        public event Func<string, float, bool> ProcessingAssetBundle;

        public event Func<string, float, bool> ProcessingBinary;

        public event Action<Platform> ProcessResourceComplete;

        public event Action<string> BuildResourceError;

        public bool Load()
        {
            if (!File.Exists(m_ConfigurationPath))
            {
                return false;
            }

            try
            {
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(m_ConfigurationPath);
                XmlNode xmlRoot = xmlDocument.SelectSingleNode("UnityGameFramework");
                XmlNode xmlEditor = xmlRoot.SelectSingleNode("ResourceBuilder");
                XmlNode xmlSettings = xmlEditor.SelectSingleNode("Settings");

                XmlNodeList xmlNodeList = null;
                XmlNode xmlNode = null;

                xmlNodeList = xmlSettings.ChildNodes;
                for (int i = 0; i < xmlNodeList.Count; i++)
                {
                    xmlNode = xmlNodeList.Item(i);
                    switch (xmlNode.Name)
                    {
                        case "InternalResourceVersion":
                            InternalResourceVersion = int.Parse(xmlNode.InnerText);
                            break;

                        case "InternalGameVersion":
                            InternalGameVersion = int.Parse(xmlNode.InnerText);
                            break;

                        case "Platforms":
                            SelectedPlatform = (Platform)int.Parse(xmlNode.InnerText);
                            break;

                        case "ZipSelected":
                            ZipSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "UncompressedAssetBundleSelected":
                            UncompressedAssetBundleSelected = bool.Parse(xmlNode.InnerText);
                            if (UncompressedAssetBundleSelected)
                            {
                                ChunkBasedCompressionSelected = false;
                            }

                            break;

                        case "DisableWriteTypeTreeSelected":
                            DisableWriteTypeTreeSelected = bool.Parse(xmlNode.InnerText);
                            if (DisableWriteTypeTreeSelected)
                            {
                                IgnoreTypeTreeChangesSelected = false;
                            }

                            break;

                        case "DeterministicAssetBundleSelected":
                            DeterministicAssetBundleSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "ForceRebuildAssetBundleSelected":
                            ForceRebuildAssetBundleSelected = bool.Parse(xmlNode.InnerText);
                            break;
                        case "OnlySingleBuildSelected":
                            OnlySingleBuildSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "IgnoreTypeTreeChangesSelected":
                            IgnoreTypeTreeChangesSelected = bool.Parse(xmlNode.InnerText);
                            if (IgnoreTypeTreeChangesSelected)
                            {
                                DisableWriteTypeTreeSelected = false;
                            }

                            break;

                        case "AppendHashToAssetBundleNameSelected":
                            AppendHashToAssetBundleNameSelected = false;
                            break;

                        case "ChunkBasedCompressionSelected":
                            ChunkBasedCompressionSelected = bool.Parse(xmlNode.InnerText);
                            if (ChunkBasedCompressionSelected)
                            {
                                UncompressedAssetBundleSelected = false;
                            }

                            break;

                        case "OutputPackageSelected":
                            OutputPackageSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "OutputFullSelected":
                            OutputFullSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "OutputPackedSelected":
                            OutputPackedSelected = bool.Parse(xmlNode.InnerText);
                            break;

                        case "BuildEventHandlerTypeName":
                            BuildEventHandlerTypeName = xmlNode.InnerText;
                            RefreshBuildEventHandler();
                            break;

                        case "OutputDirectory":
                            OutputDirectory = xmlNode.InnerText;
                            break;

                        case "SVNPath":
                            SVNPath = xmlNode.InnerText;
                            break;

                        case "OssProdMode":
                            OssProdMode = bool.Parse(xmlNode.InnerText);
                            break;
                        case "IOSAppUrl":
                            IOSAppUrl = xmlNode.InnerText;
                            break;
                        case "AndroidAppUrl":
                            AndroidAppUrl = xmlNode.InnerText;
                            break;
                    }
                }
            }
            catch
            {
                File.Delete(m_ConfigurationPath);
                return false;
            }

            return true;
        }

        public bool Save()
        {
            try
            {
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.AppendChild(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", null));

                XmlElement xmlRoot = xmlDocument.CreateElement("UnityGameFramework");
                xmlDocument.AppendChild(xmlRoot);

                XmlElement xmlBuilder = xmlDocument.CreateElement("ResourceBuilder");
                xmlRoot.AppendChild(xmlBuilder);

                XmlElement xmlSettings = xmlDocument.CreateElement("Settings");
                xmlBuilder.AppendChild(xmlSettings);

                XmlElement xmlElement = null;

                xmlElement = xmlDocument.CreateElement("InternalGameVersion");
                xmlElement.InnerText = InternalGameVersion.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("InternalResourceVersion");
                xmlElement.InnerText = InternalResourceVersion.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("Platforms");
                xmlElement.InnerText = ((int)SelectedPlatform).ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("ZipSelected");
                xmlElement.InnerText = ZipSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("UncompressedAssetBundleSelected");
                xmlElement.InnerText = UncompressedAssetBundleSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("DisableWriteTypeTreeSelected");
                xmlElement.InnerText = DisableWriteTypeTreeSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("DeterministicAssetBundleSelected");
                xmlElement.InnerText = DeterministicAssetBundleSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("ForceRebuildAssetBundleSelected");
                xmlElement.InnerText = ForceRebuildAssetBundleSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OnlySingleBuildAssetBundleSelected");
                xmlElement.InnerText = OnlySingleBuildSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("IgnoreTypeTreeChangesSelected");
                xmlElement.InnerText = IgnoreTypeTreeChangesSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("AppendHashToAssetBundleNameSelected");
                xmlElement.InnerText = AppendHashToAssetBundleNameSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("ChunkBasedCompressionSelected");
                xmlElement.InnerText = ChunkBasedCompressionSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OutputPackageSelected");
                xmlElement.InnerText = OutputPackageSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OutputFullSelected");
                xmlElement.InnerText = OutputFullSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OutputPackedSelected");
                xmlElement.InnerText = OutputPackedSelected.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("BuildEventHandlerTypeName");
                xmlElement.InnerText = BuildEventHandlerTypeName;
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OutputDirectory");
                xmlElement.InnerText = OutputDirectory;
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("SVNPath");
                xmlElement.InnerText = SVNPath;
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("OssProdMode");
                xmlElement.InnerText = OssProdMode.ToString();
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("IOSAppUrl");
                xmlElement.InnerText = IOSAppUrl;
                xmlSettings.AppendChild(xmlElement);
                xmlElement = xmlDocument.CreateElement("AndroidAppUrl");
                xmlElement.InnerText = AndroidAppUrl;
                xmlSettings.AppendChild(xmlElement);

                string configurationDirectoryName = Path.GetDirectoryName(m_ConfigurationPath);
                if (!Directory.Exists(configurationDirectoryName))
                {
                    Directory.CreateDirectory(configurationDirectoryName);
                }

                xmlDocument.Save(m_ConfigurationPath);
                AssetDatabase.Refresh();
                return true;
            }
            catch
            {
                if (File.Exists(m_ConfigurationPath))
                {
                    File.Delete(m_ConfigurationPath);
                }

                return false;
            }
        }

        public bool CopyPackage()
        {
            return Copy("Package", OutputPackagePath);
        }

        public bool CopyPacked()
        {
            return Copy("Packed", OutputPackedPath);
        }

        public bool CopyPure()
        {
            return Copy("Pure", OutputPurePath);
        }

        private bool Copy(string pre, string path)
        {
            string platform_str = SelectedPlatform.ToString();

            var src_path = Path.Combine(path, platform_str);
            if (Directory.Exists(src_path) == false)
            {
                var package_path = Utils.File.GetRegularPath(new DirectoryInfo(Utils.Text.Format("{0}/{3}/{1}_{2}/",
                    OutputDirectory, ApplicableGameVersion.Replace('.', '_'), (InternalResourceVersion).ToString(),
                    pre)).FullName);
                src_path = Path.Combine(package_path, platform_str.ToString());
                if (Directory.Exists(src_path) == false)
                {
                    return false;
                }
            }

            var tgt_path = Path.Combine(Application.streamingAssetsPath, "dat");

            //if (Directory.Exists(tgt_path) == true)
            //{
            //    Directory.Delete(tgt_path, true);
            //}

            if (Directory.Exists(tgt_path) == false)
            {
                Directory.CreateDirectory(tgt_path);
            }

            CopyFolder(src_path, tgt_path);

            EditorUtility.ClearProgressBar();

            var version_path = $"{BuildReportPath}/{platform_str}Version.txt";
            if (File.Exists(version_path) == false)
            {
                return false;
            }

            tgt_path = $"{tgt_path}/Version.txt";
            File.Copy(version_path, tgt_path, File.Exists(tgt_path));

            return true;
        }

        public bool Copy2SVN()
        {
            return CopyFull2SVN(SelectedPlatform);
        }
        //
        // public void Upload2Oss()
        // {
        //     OssManager.Instance.SwitchOssMode(OssProdMode);
        //
        //     var di = new DirectoryInfo(SVNPath);
        //     var fis = di.GetFiles("*.*", SearchOption.AllDirectories);
        //
        //     List<System.IO.FileInfo> m_list = new List<System.IO.FileInfo>();
        //
        //     for (int i = 0; i < fis.Length; i++)
        //     {
        //         var f = fis[i];
        //         var local_path = f.FullName;
        //         var remote_path = local_path.Replace(di.FullName, GameDefine.OssDirPath);
        //         remote_path = remote_path.Replace("\\", "/");
        //         EditorUtility.DisplayProgressBar("检测", remote_path, i * 1.0f / fis.Length);
        //         bool is_exist = OssManager.Instance.DoesFileExist(remote_path);
        //         if (is_exist == false)
        //         {
        //             m_list.Add(f);
        //             continue;
        //         }
        //         string local_md5;
        //         long local_length;
        //         using (var fs = File.Open(local_path, FileMode.Open))
        //         {
        //             local_md5 = OssUtils.ComputeContentMd5(fs, fs.Length);
        //             local_length = fs.Length;
        //         }
        //         var oss_md5 = OssManager.Instance.GetFileMD5(remote_path);
        //         var oss_length = OssManager.Instance.GetFileLength(remote_path);
        //
        //         if (local_md5 == oss_md5 && local_length == oss_length)
        //         {
        //             continue;
        //         }
        //         m_list.Add(f);
        //     }
        //     EditorUtility.ClearProgressBar();
        //
        //     if (m_list.Count == 0)
        //     {
        //         Debug.LogError("资源已提交");
        //     }
        //     else
        //     {
        //         for (int i = 0; i < m_list.Count; i++)
        //         {
        //             var f = m_list[i];
        //             var local_path = f.FullName;
        //             var remote_path = local_path.Replace(di.FullName, GameDefine.OssDirPath);
        //             remote_path = remote_path.Replace("\\", "/");
        //             Debug.LogError($"提交：{remote_path}");
        //
        //             EditorUtility.DisplayProgressBar($"正在提交({i}/{m_list.Count})", remote_path, i * 1.0f / m_list.Count);
        //             OssManager.Instance.UploadFile(remote_path, local_path);
        //         }
        //         EditorUtility.ClearProgressBar();
        //     }
        //
        //     string platform_dir = $"{SVNPath}/{SelectedPlatform}";
        //     if (Directory.Exists(platform_dir) == false)
        //     {
        //         return;
        //     }
        //     string version_dir = $"{platform_dir}/Version";
        //     DirectoryInfo vdi = new DirectoryInfo(version_dir);
        //     var vfi = vdi.GetFiles()[0];
        //     string name = vfi.Name;
        //     string version_id = name.Replace("Version_", "").Replace(".txt", "");
        //     string version_id_path = $"{GameDefine.OssDirPath}/{SelectedPlatform}/VersionId.txt";
        //     Debug.LogError($"version_id = {version_id}");
        //     Debug.LogError($"version_id_path = {version_id_path}");
        //     OssManager.Instance.AppendString(version_id_path, version_id);
        // }

        public void PrintBundleSize()
        {
            string path = Path.Combine(WorkingPath, SelectedPlatform.ToString());

            DirectoryInfo di = new DirectoryInfo(path);
            Debug.LogError(di.Exists);
            if (di.Exists == false)
            {
                return;
            }

            var fis = di.GetFiles("*.*", SearchOption.AllDirectories);

            List<System.IO.FileInfo> m_list = new List<System.IO.FileInfo>();
            foreach (var f in fis)
            {
                if (f.Extension == ".manifest")
                {
                    continue;
                }

                m_list.Add(f);
            }

            m_list.Sort((a, b) =>
            {
                if (a.Length > b.Length)
                {
                    return -1;
                }
                else if (a.Length < b.Length)
                {
                    return 1;
                }

                return 0;
            });
            foreach (var f in m_list)
            {
                long kb = f.Length / 1024;
                long mb = kb / 1024;
                if (mb > 0)
                {
                    Debug.LogError($"name = {f.FullName}; length = {mb}MB({f.Length});");
                }
                else
                {
                    Debug.LogError($"name = {f.FullName}; length = {kb}KB({f.Length}); ");
                }
            }
        }

        public void CheckBundleDistribute()
        {
            var platform = SelectedPlatform.ToString();
            m_ABDistributionChecker.StartCheck(WorkingPath, platform, platform, (file_path) =>
            {
                var path = Path.Combine(Application.dataPath, @"../Tools/TextFile2Excel/Converter.exe");
                OpenEXE(path, "distribution", file_path);
            });
        }

        /// <summary>
        /// 检测资源冗余
        /// </summary>
        public void CheckRedundancy()
        {
            var platform = SelectedPlatform.ToString();
            m_ABRedundancyChecker.StartCheck(WorkingPath, platform, platform, (file_path) =>
            {
                var path = Path.Combine(Application.dataPath, @"../Tools/TextFile2Excel/Converter.exe");
                OpenEXE(path, "redundancy", file_path);
            });
        }

        public void Test() { }

        private void OpenEXE(string path, string operate, string file_path)
        {
            try
            {
                System.Diagnostics.Process myprocess = new System.Diagnostics.Process();
                System.Diagnostics.ProcessStartInfo startInfo =
                    new System.Diagnostics.ProcessStartInfo(path, $"{operate} {file_path}");
                myprocess.StartInfo = startInfo;
                myprocess.StartInfo.UseShellExecute = false;
                myprocess.StartInfo.CreateNoWindow = false;
                myprocess.Start();

                myprocess.WaitForExit();

                if (myprocess.ExitCode == 0)
                {
                    UnityEngine.Debug.Log("完成");
                }
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.LogError("Error：" + ex.Message);
            }
        }

        private bool CopyFull2SVN(Platform platform)
        {
            //     var src_path = Path.Combine(OutputFullPath, platform.ToString());
            //     if (Directory.Exists(src_path) == false)
            //     {
            //         return false;
            //     }
            //
            //     if (Directory.Exists(SVNPath) == true)
            //     {
            //         Directory.Delete(SVNPath, true);
            //     }
            //     if (Directory.Exists(SVNPath) == false)
            //     {
            //         Directory.CreateDirectory(SVNPath);
            //     }
            //
            //     var version_path = $"{BuildReportPath}/{platform}Version.txt";
            //     if (File.Exists(version_path) == false)
            //     {
            //         return false;
            //     }
            //     var json = File.ReadAllText(version_path);
            //     var version_info = JsonUtility.FromJson<VersionInfo>(json);
            //     var game_version = version_info.InternalGameVersion;
            //     var res_version = version_info.InternalResourceVersion;
            //
            //     string tgt_path = $"{SVNPath}/{platform}/{game_version}";
            //     if (Directory.Exists(tgt_path) == false)
            //     {
            //         Directory.CreateDirectory(tgt_path);
            //     }
            //     CopyFolder(src_path, tgt_path);
            //     EditorUtility.ClearProgressBar();
            //
            //     var version_tgt_path = $"{SVNPath}/{platform}/Version/Version_{game_version}_{res_version}.txt";
            //
            //     System.IO.FileInfo fi = new System.IO.FileInfo(version_tgt_path);
            //     if (Directory.Exists(fi.DirectoryName) == false)
            //     {
            //         Directory.CreateDirectory(fi.DirectoryName);
            //     }
            //
            //     File.Copy(version_path, version_tgt_path, File.Exists(version_tgt_path));
            //
            //     Debug.Log($"Copy {platform}Version.txt Success");
            return true;
        }

        private void CopyFolder(string src_path, string tgt_path)
        {
            if (Directory.Exists(tgt_path) == false)
            {
                Directory.CreateDirectory(tgt_path);
            }

            CopyFile(src_path, tgt_path);
            string[] directionName = Directory.GetDirectories(src_path);
            foreach (string dirPath in directionName)
            {
                string directionPathTemp = tgt_path + "/" + dirPath.Substring(src_path.Length + 1);
                CopyFolder(dirPath, directionPathTemp);
            }
        }

        private void CopyFile(string src_path, string tgt_path)
        {
            // string[] filesList = Directory.GetFiles(src_path);
            // foreach (string s in filesList)
            // {
            //     string depart = SelectedPlatform == Platform.IOS ? "/" : "/";
            //     string t = tgt_path + depart + s.Substring(src_path.Length + 1);
            //     EditorUtility.DisplayProgressBar("拷贝文件", s, 1);
            //     if (File.Exists(t))
            //     {
            //         string s_md5, t_md5;
            //         long s_len, t_len;
            //         using (var fs = File.Open(s, FileMode.Open))
            //         {
            //             s_md5 = OssUtils.ComputeContentMd5(fs, fs.Length);
            //             s_len = fs.Length;
            //         }
            //         using (var fs = File.Open(t, FileMode.Open))
            //         {
            //             t_md5 = OssUtils.ComputeContentMd5(fs, fs.Length);
            //             t_len = fs.Length;
            //         }
            //         if (s_md5 == t_md5 && s_len == t_len)
            //         {
            //             continue;
            //         }
            //         File.Copy(s, t, true);
            //     }
            //     else
            //     {
            //         File.Copy(s, t);
            //     }
            // }
        }

        public string GetMD5HashFromFile(FileStream file)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            //去除FileSystem 中的数据头长度 使用了随机数填充字节数组
            var head = 20 + 12 + 256;
            byte[] bData = new byte[file.Length - head];
            file.Seek(head, SeekOrigin.Begin);
            file.Read(bData, 0, bData.Length);
            byte[] retVal = md5.ComputeHash(bData);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }

            return sb.ToString();
        }

        public void SetBuildEventHandler(IBuildEventHandler buildEventHandler)
        {
            m_BuildEventHandler = buildEventHandler;
        }

        public string[] GetBuildEventHandlerTypeNames()
        {
            return m_BuildEventHandlerTypeNames.ToArray();
        }

        public bool IsPlatformSelected(Platform platform)
        {
            return SelectedPlatform == platform;
        }

        public void SelectPlatform(Platform platform, bool selected)
        {
            if (selected)
            {
                SelectedPlatform = platform;
            }
        }

        public bool RefreshBuildEventHandler()
        {
            bool retVal = false;
            // if (!string.IsNullOrEmpty(BuildEventHandlerTypeName) && m_BuildEventHandlerTypeNames.Contains(BuildEventHandlerTypeName))
            // {
            //     System.Type buildEventHandlerType = Utils.Assembly.GetType(BuildEventHandlerTypeName);
            //     if (buildEventHandlerType != null)
            //     {
            //         IBuildEventHandler buildEventHandler = (IBuildEventHandler)Activator.CreateInstance(buildEventHandlerType);
            //         if (buildEventHandler != null)
            //         {
            //             SetBuildEventHandler(buildEventHandler);
            //             return true;
            //         }
            //     }
            // }
            // else
            // {
            //     retVal = true;
            // }
            //
            // BuildEventHandlerTypeName = string.Empty;
            // SetBuildEventHandler(null);
            return retVal;
        }

        public bool BuildResources()
        {
            if (!IsValidOutputDirectory)
            {
                return false;
            }

            if (SelectedPlatform == Platform.Undefined)
            {
                throw new Exception("Platform undefined.");
            }

            if (m_BuildEventHandler == null)
            {
                throw new Exception("Execute build event handler 'OnPostprocessAllPlatforms'...");
            }


            if (Directory.Exists(OutputFullPath))
            {
                Directory.Delete(OutputFullPath, true);
            }

            Directory.CreateDirectory(OutputFullPath);

            if (Directory.Exists(OutputPackagePath))
            {
                Directory.Delete(OutputPackagePath, true);
            }

            Directory.CreateDirectory(OutputPackagePath);

            if (Directory.Exists(OutputPackedPath))
            {
                Directory.Delete(OutputPackedPath, true);
            }

            Directory.CreateDirectory(OutputPackedPath);

            if (Directory.Exists(OutputPurePath))
            {
                Directory.Delete(OutputPurePath, true);
            }

            Directory.CreateDirectory(OutputPurePath);

            if (Directory.Exists(BuildReportPath))
            {
                Directory.Delete(BuildReportPath, true);
            }

            Directory.CreateDirectory(BuildReportPath);

            BuildAssetBundleOptions buildAssetBundleOptions = GetBuildAssetBundleOptions();
            m_BuildReport.Initialize(BuildReportPath, ProductName, CompanyName, GameIdentifier, GameFrameworkVersion,
                UnityVersion, ApplicableGameVersion,
                InternalResourceVersion, SelectedPlatform, ZipSelected, (int)buildAssetBundleOptions, m_ResourceDatas);

            try
            {
                m_BuildReport.LogInfo("Build Start Time: {0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

                m_BuildReport.LogInfo("Execute build event handler 'OnPreprocessAllPlatforms'...");
                m_BuildEventHandler.OnPreprocessAllPlatforms(this);

                m_BuildReport.LogInfo("Start prepare resource collection...");
                if (!m_ResourceCollection.Load())
                {
                    m_BuildReport.LogError(
                        "Can not parse 'ResourceCollection.xml', please use 'Resource Editor' tool first.");

                    m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessAllPlatforms'...");
                    m_BuildEventHandler.OnPostprocessAllPlatforms(this);

                    m_BuildReport.SaveReport();
                    return false;
                }

                m_BuildReport.LogInfo("Prepare resource collection complete.");
                m_BuildReport.LogInfo("Start analyze assets dependency...");

                m_ResourceAnalyzerController.Analyze(OnlySingleBuildSelected);

                m_BuildReport.LogInfo("Analyze assets dependency complete.");
                m_BuildReport.LogInfo("Start prepare build data...");

                AssetBundleBuild[] assetBundleBuildDatas = null;
                ResourceData[] assetBundleResourceDatas = null;
                if (!PrepareBuildData(out assetBundleBuildDatas, out assetBundleResourceDatas))
                {
                    m_BuildReport.LogError("Prepare resource build data failure.");

                    m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessAllPlatforms'...");
                    m_BuildEventHandler.OnPostprocessAllPlatforms(this);

                    m_BuildReport.SaveReport();
                    return false;
                }

                m_BuildReport.LogInfo("Prepare resource build data complete.");
                m_BuildReport.LogInfo("Start build resources for selected platforms...");

                BuildResources(SelectedPlatform, assetBundleBuildDatas, buildAssetBundleOptions,
                    assetBundleResourceDatas);

                m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessAllPlatforms'...");
                m_BuildEventHandler.OnPostprocessAllPlatforms(this);

                m_BuildReport.LogInfo("Build resources for selected platforms complete.");
                m_BuildReport.SaveReport();

                return true;
            }
            catch (Exception exception)
            {
                string errorMessage = exception.ToString();
                m_BuildReport.LogFatal(errorMessage);
                m_BuildReport.SaveReport();
                if (BuildResourceError != null)
                {
                    BuildResourceError(errorMessage);
                }

                return false;
            }
        }

        private string m_working_path;
        private string m_full_path;
        private string m_package_path;
        private string m_packed_path;
        private string m_pure_path;

        private bool BuildResources(Platform platform, AssetBundleBuild[] assetBundleBuildDatas,
            BuildAssetBundleOptions buildAssetBundleOptions, ResourceData[] assetBundleResourceDatas)
        {
            string platformName = platform.ToString();
            m_BuildReport.LogInfo("Start build resources for '{0}'...", platformName);

            m_working_path = Utils.Text.Format("{0}{1}/", WorkingPath, platformName);
            m_BuildReport.LogInfo("Working path is '{0}'.", m_working_path);

            m_package_path = Utils.Text.Format("{0}{1}/", OutputPackagePath, platformName);
            if (OutputPackageSelected)
            {
                Directory.CreateDirectory(m_package_path);
                m_BuildReport.LogInfo("Output package is selected, path is '{0}'.", m_package_path);
            }
            else
            {
                m_BuildReport.LogInfo("Output package is not selected.");
            }

            m_full_path = Utils.Text.Format("{0}{1}/", OutputFullPath, platformName);
            if (OutputFullSelected)
            {
                Directory.CreateDirectory(m_full_path);
                m_BuildReport.LogInfo("Output full is selected, path is '{0}'.", m_full_path);
            }
            else
            {
                m_BuildReport.LogInfo("Output full is not selected.");
            }

            m_packed_path = Utils.Text.Format("{0}{1}/", OutputPackedPath, platformName);
            if (OutputPackedSelected)
            {
                Directory.CreateDirectory(m_packed_path);
                m_BuildReport.LogInfo("Output packed is selected, path is '{0}'.", m_packed_path);
            }
            else
            {
                m_BuildReport.LogInfo("Output packed is not selected.");
            }

            m_pure_path = Utils.Text.Format("{0}{1}/", OutputPurePath, platformName);
            if (OutputPureSelected)
            {
                Directory.CreateDirectory(m_pure_path);
                m_BuildReport.LogInfo("Output pure is selected, path is '{0}'.", m_pure_path);
            }
            else
            {
                m_BuildReport.LogInfo("Output pure is not selected.");
            }

            // Clean working path
            List<string> validNames = new List<string>();
            foreach (ResourceData assetBundleResourceData in assetBundleResourceDatas)
            {
                validNames.Add(GetResourceFullName(assetBundleResourceData.Name, assetBundleResourceData.Variant)
                    .ToLower());
            }

            if (Directory.Exists(m_working_path))
            {
                Uri workingUri = new Uri(m_working_path, UriKind.Absolute);
                string[] fileNames = Directory.GetFiles(m_working_path, "*", SearchOption.AllDirectories);
                foreach (string fileName in fileNames)
                {
                    if (fileName.EndsWith(".manifest", StringComparison.Ordinal))
                    {
                        continue;
                    }

                    string relativeName = workingUri.MakeRelativeUri(new Uri(fileName, UriKind.Absolute)).ToString();
                    if (!validNames.Contains(relativeName))
                    {
                        File.Delete(fileName);
                    }
                }

                string[] manifestNames = Directory.GetFiles(m_working_path, "*.manifest", SearchOption.AllDirectories);
                foreach (string manifestName in manifestNames)
                {
                    if (!File.Exists(manifestName.Substring(0, manifestName.LastIndexOf('.'))))
                    {
                        File.Delete(manifestName);
                    }
                }

                // Utils.File.RemoveEmptyDirectory(m_working_path);
            }

            if (!Directory.Exists(m_working_path))
            {
                Directory.CreateDirectory(m_working_path);
            }

            m_BuildReport.LogInfo("Execute build event handler 'OnPreprocessPlatform' for '{0}'...", platformName);
            m_BuildEventHandler.OnPreprocessPlatform(this);

            // ShaderBuildProcessor.OnBeginBuildAssetBundles();

            // Build AssetBundles
            m_BuildReport.LogInfo("Unity start build asset bundles for '{0}'...", platformName);
            AssetBundleManifest assetBundleManifest = BuildPipeline.BuildAssetBundles(m_working_path,
                assetBundleBuildDatas, buildAssetBundleOptions, GetBuildTarget(platform));
            if (assetBundleManifest == null)
            {
                m_BuildReport.LogError("Build asset bundles for '{0}' failure.", platformName);

                m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessPlatform' for '{0}'...", platformName);
                m_BuildEventHandler.OnPostprocessPlatform(this, false);

                return false;
            }

            // ShaderBuildProcessor.OnEndBuildAssetBundles();

            m_BuildReport.LogInfo("Execute build event handler 'OnBuildAssetBundlesComplete' for '{0}'...",
                platformName);
            m_BuildEventHandler.OnBuildAssetBundlesComplete(this, assetBundleManifest);

            m_BuildReport.LogInfo("Unity build asset bundles for '{0}' complete.", platformName);

            m_BuildReport.LogInfo("Create file system for '{0}' complete.", platformName);

            // Process AssetBundles
            for (int i = 0; i < assetBundleResourceDatas.Length; i++)
            {
                var data = assetBundleResourceDatas[i];
                string fullName = GetResourceFullName(data.Name, data.Variant);
                if (ProcessingAssetBundle != null)
                {
                    if (ProcessingAssetBundle(fullName, (float)(i + 1) / assetBundleResourceDatas.Length))
                    {
                        m_BuildReport.LogWarning("The build has been canceled by user.");

                        m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessPlatform' for '{0}'...",
                            platformName);
                        m_BuildEventHandler.OnPostprocessPlatform(this, false);

                        return false;
                    }
                }

                m_BuildReport.LogInfo("Start process asset bundle '{0}' for '{1}'...", fullName, platformName);

                // if (!ProcessAssetBundle(platform, ZipSelected, data.Name, data.Variant, data.FileSystem))
                // {
                //     return false;
                // }

                m_BuildReport.LogInfo("Process asset bundle '{0}' for '{1}' complete.", fullName, platformName);
            }

            if (OutputPackageSelected)
            {
                // ProcessPackageVersionList(m_package_path, platform);
                m_BuildReport.LogInfo("Process package version list for '{0}' complete.", platformName);
            }

            // if (OutputFullSelected)
            // {
            //     VersionListData versionListData = ProcessUpdatableVersionList(m_full_path, platform);
            //     m_BuildReport.LogInfo("Process updatable version list for '{0}' complete, updatable version list path is '{1}', length is '{2}', hash code is '{3}[0x{3:X8}]', zip length is '{4}', zip hash code is '{5}[0x{5:X8}]'.", platformName, versionListData.Path, versionListData.Length.ToString(), versionListData.HashCode, versionListData.ZipLength.ToString(), versionListData.ZipHashCode);
            //
            //     m_BuildReport.LogInfo("Execute build event handler 'OnOutputUpdatableVersionListData' for '{0}'...", platformName);
            //     m_BuildEventHandler.OnOutputUpdatableVersionListData(platform, versionListData.Path, versionListData.Length, versionListData.HashCode, versionListData.ZipLength, versionListData.ZipHashCode,
            //         ApplicableGameVersion, InternalGameVersion, InternalResourceVersion, "", OssProdMode, IOSAppUrl, AndroidAppUrl, BuildReportPath, SdkChannel);
            // }
            //
            // if (OutputPackedSelected)
            // {
            //     ProcessReadOnlyVersionList(m_packed_path, platform);
            //     m_BuildReport.LogInfo("Process read only version list for '{0}' complete.", platformName);
            // }
            //
            // if (OutputPureSelected)
            // {
            //     ProcessPureVersionList(m_pure_path, platform);
            //     m_BuildReport.LogInfo("Process read only version list for '{0}' complete.", platformName);
            // }

            m_BuildReport.LogInfo("Execute build event handler 'OnPostprocessPlatform' for '{0}'...", platformName);
            m_BuildEventHandler.OnPostprocessPlatform(this, true);

            if (ProcessResourceComplete != null)
            {
                ProcessResourceComplete(platform);
            }

            m_BuildReport.LogInfo("Build resources for '{0}' success.", platformName);
            return true;
        }

        // private bool ProcessAssetBundle(Platform platform, bool zip, string name, string variant, string fileSystem)
        // {
        //     string fullName = GetResourceFullName(name, variant);
        //     ResourceData resourceData = m_ResourceDatas[fullName];
        //     string workingName = Utils.File.GetRegularPath(Path.Combine(m_working_path, fullName.ToLower()));
        //
        //     byte[] bytes = File.ReadAllBytes(workingName);
        //     int length = bytes.Length;
        //     int hashCode = Utils.Verifier.GetCrc32(bytes);
        //     int zipLength = length;
        //     int zipHashCode = hashCode;
        //
        //     byte[] hashBytes = Utils.Converter.GetBytes(hashCode);
        //     if (resourceData.LoadType == LoadType.LoadFromMemoryAndQuickDecrypt)
        //     {
        //         bytes = Utils.Encryption.GetQuickXorBytes(bytes, hashBytes);
        //     }
        //     else if (resourceData.LoadType == LoadType.LoadFromMemoryAndDecrypt)
        //     {
        //         bytes = Utils.Encryption.GetXorBytes(bytes, hashBytes);
        //     }
        //
        //     return ProcessOutput(platform, zip, name, variant, fileSystem, resourceData, bytes, length, hashCode, zipLength, zipHashCode);
        // }
        //
        // private void ProcessPackageVersionList(string outputPackagePath, Platform platform)
        // {
        //     Asset[] originalAssets = m_ResourceCollection.GetAssets();
        //     PackageVersionList.Asset[] assets = new PackageVersionList.Asset[originalAssets.Length];
        //     for (int i = 0; i < assets.Length; i++)
        //     {
        //         Asset originalAsset = originalAssets[i];
        //         assets[i] = new PackageVersionList.Asset(originalAsset.Path, GetDependencyAssetIndexes(originalAsset.Path));
        //     }
        //
        //     SortedDictionary<string, ResourceData>.ValueCollection resourceDatas = m_ResourceDatas.Values;
        //
        //     int index = 0;
        //     PackageVersionList.Resource[] resources = new PackageVersionList.Resource[m_ResourceCollection.ResourceCount];
        //     foreach (ResourceData resourceData in resourceDatas)
        //     {
        //         ResourceCode resourceCode = resourceData.GetCode(platform);
        //         resources[index++] = new PackageVersionList.Resource(resourceData.Name, resourceData.Variant, GetExtension(resourceData), (byte)resourceData.LoadType, resourceCode.Length, resourceCode.HashCode, GetAssetIndexes(resourceData));
        //     }
        //
        //     string[] fileSystemNames = GetFileSystemNames(resourceDatas);
        //     PackageVersionList.FileSystem[] fileSystems = new PackageVersionList.FileSystem[fileSystemNames.Length];
        //     for (int i = 0; i < fileSystems.Length; i++)
        //     {
        //         fileSystems[i] = new PackageVersionList.FileSystem(fileSystemNames[i], GetResourceIndexesFromFileSystem(resourceDatas, fileSystemNames[i]));
        //     }
        //
        //     string[] resourceGroupNames = GetResourceGroupNames(resourceDatas);
        //     PackageVersionList.ResourceGroup[] resourceGroups = new PackageVersionList.ResourceGroup[resourceGroupNames.Length];
        //     for (int i = 0; i < resourceGroups.Length; i++)
        //     {
        //         resourceGroups[i] = new PackageVersionList.ResourceGroup(resourceGroupNames[i], GetResourceIndexesFromResourceGroup(resourceDatas, resourceGroupNames[i]));
        //     }
        //
        //     PackageVersionList versionList = new PackageVersionList(ApplicableGameVersion, InternalResourceVersion, assets, resources, fileSystems, resourceGroups);
        //     PackageVersionListSerializer serializer = new PackageVersionListSerializer();
        //     string packageVersionListPath = Utils.File.GetRegularPath(Path.Combine(outputPackagePath, RemoteVersionListFileName));
        //     using (FileStream fileStream = new FileStream(packageVersionListPath, FileMode.Create, FileAccess.Write))
        //     {
        //         if (!serializer.Serialize(fileStream, versionList))
        //         {
        //             throw new GeneralException("Serialize package version list failure.");
        //         }
        //     }
        // }
        //
        // private VersionListData ProcessUpdatableVersionList(string outputFullPath, Platform platform)
        // {
        //     Asset[] originalAssets = m_ResourceCollection.GetAssets();
        //     UpdatableVersionList.Asset[] assets = new UpdatableVersionList.Asset[originalAssets.Length];
        //     for (int i = 0; i < assets.Length; i++)
        //     {
        //         Asset originalAsset = originalAssets[i];
        //         assets[i] = new UpdatableVersionList.Asset(originalAsset.Path, GetDependencyAssetIndexes(originalAsset.Path));
        //     }
        //
        //     SortedDictionary<string, ResourceData>.ValueCollection resourceDatas = m_ResourceDatas.Values;
        //
        //     int index = 0;
        //     UpdatableVersionList.Resource[] resources = new UpdatableVersionList.Resource[m_ResourceCollection.ResourceCount];
        //     foreach (ResourceData resourceData in resourceDatas)
        //     {
        //         ResourceCode resourceCode = resourceData.GetCode(platform);
        //         resources[index++] = new UpdatableVersionList.Resource(resourceData.Name, resourceData.Variant, GetExtension(resourceData), (byte)resourceData.LoadType, resourceCode.Length, resourceCode.HashCode, resourceCode.ZipLength, resourceCode.ZipHashCode, GetAssetIndexes(resourceData));
        //     }
        //
        //     string[] fileSystemNames = GetFileSystemNames(resourceDatas);
        //     UpdatableVersionList.FileSystem[] fileSystems = new UpdatableVersionList.FileSystem[fileSystemNames.Length];
        //     for (int i = 0; i < fileSystems.Length; i++)
        //     {
        //         fileSystems[i] = new UpdatableVersionList.FileSystem(fileSystemNames[i], GetResourceIndexesFromFileSystem(resourceDatas, fileSystemNames[i]));
        //     }
        //
        //     string[] resourceGroupNames = GetResourceGroupNames(resourceDatas);
        //     UpdatableVersionList.ResourceGroup[] resourceGroups = new UpdatableVersionList.ResourceGroup[resourceGroupNames.Length];
        //     for (int i = 0; i < resourceGroups.Length; i++)
        //     {
        //         resourceGroups[i] = new UpdatableVersionList.ResourceGroup(resourceGroupNames[i], GetResourceIndexesFromResourceGroup(resourceDatas, resourceGroupNames[i]));
        //     }
        //
        //     UpdatableVersionList versionList = new UpdatableVersionList(ApplicableGameVersion, InternalResourceVersion, assets, resources, fileSystems, resourceGroups);
        //     UpdatableVersionListSerializer serializer = new UpdatableVersionListSerializer();
        //     string updatableVersionListPath = Utils.File.GetRegularPath(Path.Combine(outputFullPath, RemoteVersionListFileName));
        //     using (FileStream fileStream = new FileStream(updatableVersionListPath, FileMode.Create, FileAccess.Write))
        //     {
        //         if (!serializer.Serialize(fileStream, versionList))
        //         {
        //             throw new GeneralException("Serialize updatable version list failure.");
        //         }
        //     }
        //
        //     byte[] bytes = File.ReadAllBytes(updatableVersionListPath);
        //     int length = bytes.Length;
        //     int hashCode = Utils.Verifier.GetCrc32(bytes);
        //     bytes = Utils.Zip.Compress(bytes);
        //     int zipLength = bytes.Length;
        //     File.WriteAllBytes(updatableVersionListPath, bytes);
        //     int zipHashCode = Utils.Verifier.GetCrc32(bytes);
        //     int dotPosition = RemoteVersionListFileName.LastIndexOf('.');
        //     string versionListFullNameWithCrc32 = Utils.Text.Format("{0}.{2:x8}.{1}", RemoteVersionListFileName.Substring(0, dotPosition), RemoteVersionListFileName.Substring(dotPosition + 1), hashCode);
        //     string updatableVersionListPathWithCrc32 = Utils.File.GetRegularPath(Path.Combine(outputFullPath, versionListFullNameWithCrc32));
        //     File.Move(updatableVersionListPath, updatableVersionListPathWithCrc32);
        //
        //     return new VersionListData(updatableVersionListPathWithCrc32, length, hashCode, zipLength, zipHashCode);
        // }

        // private void ProcessReadOnlyVersionList(string outputPackedPath, Platform platform)
        // {
        //     ResourceData[] packedResourceDatas = GetPackedResourceDatas();
        //
        //     LocalVersionList.Resource[] resources = new LocalVersionList.Resource[packedResourceDatas.Length];
        //     for (int i = 0; i < resources.Length; i++)
        //     {
        //         ResourceData resourceData = packedResourceDatas[i];
        //         ResourceCode resourceCode = resourceData.GetCode(platform);
        //         resources[i] = new LocalVersionList.Resource(resourceData.Name, resourceData.Variant, GetExtension(resourceData), (byte)resourceData.LoadType, resourceCode.Length, resourceCode.HashCode);
        //     }
        //
        //     string[] packedFileSystemNames = GetFileSystemNames(packedResourceDatas);
        //     LocalVersionList.FileSystem[] fileSystems = new LocalVersionList.FileSystem[packedFileSystemNames.Length];
        //     for (int i = 0; i < fileSystems.Length; i++)
        //     {
        //         fileSystems[i] = new LocalVersionList.FileSystem(packedFileSystemNames[i], GetResourceIndexesFromFileSystem(packedResourceDatas, packedFileSystemNames[i]));
        //     }
        //
        //     LocalVersionList versionList = new LocalVersionList(resources, fileSystems);
        //     LocalVersionListSerializer serializer = new LocalVersionListSerializer(true);
        //     string readOnlyVersionListPath = Utils.File.GetRegularPath(Path.Combine(outputPackedPath, LocalVersionListFileName));
        //     using (FileStream fileStream = new FileStream(readOnlyVersionListPath, FileMode.Create, FileAccess.Write))
        //     {
        //         if (!serializer.Serialize(fileStream, versionList))
        //         {
        //             throw new GeneralException("Serialize read only version list failure.");
        //         }
        //     }
        // }
        //
        // private void ProcessPureVersionList(string outputPurePath, Platform platform)
        // {
        //     LocalVersionList.Resource[] resources = new LocalVersionList.Resource[0];
        //     LocalVersionList.FileSystem[] fileSystems = new LocalVersionList.FileSystem[0];
        //
        //     LocalVersionList versionList = new LocalVersionList(resources, fileSystems);
        //     LocalVersionListSerializer serializer = new LocalVersionListSerializer(true);
        //     string readOnlyVersionListPath = Utils.File.GetRegularPath(Path.Combine(outputPurePath, LocalVersionListFileName));
        //     using (FileStream fileStream = new FileStream(readOnlyVersionListPath, FileMode.Create, FileAccess.Write))
        //     {
        //         if (!serializer.Serialize(fileStream, versionList))
        //         {
        //             throw new GeneralException("Serialize read only version list failure.");
        //         }
        //     }
        // }

        private int[] GetDependencyAssetIndexes(string assetName)
        {
            List<int> dependencyAssetIndexes = new List<int>();
            Asset[] assets = m_ResourceCollection.GetAssets();
            DependencyData dependencyData = m_ResourceAnalyzerController.GetDependencyData(assetName);
            foreach (Asset dependencyAsset in dependencyData.GetDependencyAssets())
            {
                for (int i = 0; i < assets.Length; i++)
                {
                    if (assets[i] == dependencyAsset)
                    {
                        dependencyAssetIndexes.Add(i);
                        break;
                    }
                }
            }

            dependencyAssetIndexes.Sort();
            return dependencyAssetIndexes.ToArray();
        }

        private int[] GetAssetIndexes(ResourceData resourceData)
        {
            Asset[] assets = m_ResourceCollection.GetAssets();
            string[] assetGuids = resourceData.GetAssetGuids();
            int[] assetIndexes = new int[assetGuids.Length];
            for (int i = 0; i < assetGuids.Length; i++)
            {
                assetIndexes[i] = Array.BinarySearch(assets, m_ResourceCollection.GetAsset(assetGuids[i]));
                if (assetIndexes[i] < 0)
                {
                    throw new Exception("Asset is invalid.");
                }
            }

            return assetIndexes;
        }

        private ResourceData[] GetPackedResourceDatas()
        {
            List<ResourceData> packedResourceDatas = new List<ResourceData>();
            foreach (ResourceData resourceData in m_ResourceDatas.Values)
            {
                if (!resourceData.Packed)
                {
                    continue;
                }

                packedResourceDatas.Add(resourceData);
            }

            return packedResourceDatas.ToArray();
        }

        private string[] GetFileSystemNames(IEnumerable<ResourceData> resourceDatas)
        {
            HashSet<string> fileSystemNames = new HashSet<string>();
            foreach (ResourceData resourceData in resourceDatas)
            {
                if (resourceData.FileSystem == null)
                {
                    continue;
                }

                fileSystemNames.Add(resourceData.FileSystem);
            }

            return fileSystemNames.OrderBy(x => x).ToArray();
        }

        private string GetFileSystemName(ResourceData resourceData)
        {
            return resourceData.FileSystem;
        }

        private int[] GetResourceIndexesFromFileSystem(IEnumerable<ResourceData> resourceDatas, string fileSystemName)
        {
            int index = 0;
            List<int> resourceIndexes = new List<int>();
            foreach (ResourceData resourceData in resourceDatas)
            {
                if (resourceData.FileSystem == fileSystemName)
                {
                    resourceIndexes.Add(index);
                }

                index++;
            }

            resourceIndexes.Sort();
            return resourceIndexes.ToArray();
        }

        private string[] GetResourceGroupNames(IEnumerable<ResourceData> resourceDatas)
        {
            HashSet<string> resourceGroupNames = new HashSet<string>();
            foreach (ResourceData resourceData in resourceDatas)
            {
                foreach (string resourceGroup in resourceData.GetResourceGroups())
                {
                    resourceGroupNames.Add(resourceGroup);
                }
            }

            return resourceGroupNames.OrderBy(x => x).ToArray();
        }

        private int[] GetResourceIndexesFromResourceGroup(IEnumerable<ResourceData> resourceDatas,
            string resourceGroupName)
        {
            int index = 0;
            List<int> resourceIndexes = new List<int>();
            foreach (ResourceData resourceData in resourceDatas)
            {
                foreach (string resourceGroup in resourceData.GetResourceGroups())
                {
                    if (resourceGroup == resourceGroupName)
                    {
                        resourceIndexes.Add(index);
                        break;
                    }
                }

                index++;
            }

            resourceIndexes.Sort();
            return resourceIndexes.ToArray();
        }

        // private void CreateFileSystems(IEnumerable<ResourceData> resourceDatas, string outputPath, Dictionary<string, FileSystem> outputFileSystem)
        // {
        //     string[] fileSystemNames = GetFileSystemNames(resourceDatas);
        //
        //     foreach (string fileSystemName in fileSystemNames)
        //     {
        //         int fileCount = GetResourceIndexesFromFileSystem(resourceDatas, fileSystemName).Length;
        //         string fullPath = Utils.File.GetRegularPath(Path.Combine(outputPath, Utils.Text.Format("{0}.{1}", fileSystemName, DefaultExtension)));
        //         string directory = Path.GetDirectoryName(fullPath);
        //         if (!Directory.Exists(directory))
        //         {
        //             Directory.CreateDirectory(directory);
        //         }
        //
        //         FileSystem fileSystem = FileSystemManager.Instance.CreateFileSystem(fullPath, FileSystemAccess.Write, fileCount, fileCount);
        //         outputFileSystem.Add(fileSystemName, fileSystem);
        //     }
        // }

        // private FileSystem CreateFileSystem(ResourceData resourceData, string outputPath)
        // {
        //     string fileSystemName = GetFileSystemName(resourceData);
        //
        //     int fileCount = 1;
        //     string fullPath = Utils.File.GetRegularPath(Path.Combine(outputPath, Utils.Text.Format("{0}.{1}", fileSystemName, DefaultExtension)));
        //     string directory = Path.GetDirectoryName(fullPath);
        //     if (!Directory.Exists(directory))
        //     {
        //         Directory.CreateDirectory(directory);
        //     }
        //
        //     FileSystem fileSystem = FileSystemManager.Instance.CreateFileSystem(fullPath, FileSystemAccess.Write, fileCount, fileCount);
        //
        //     return fileSystem;
        // }

        // private bool ProcessOutput(Platform platform, bool zip, string name, string variant, string fileSystem, ResourceData resourceData, byte[] bytes, int length, int hashCode, int zipLength, int zipHashCode)
        // {
        //     string fullNameWithExtension = Utils.Text.Format("{0}.{1}", GetResourceFullName(name, variant), GetExtension(resourceData));
        //
        //     if (OutputPackageSelected)
        //     {
        //         if (string.IsNullOrEmpty(fileSystem))
        //         {
        //             string packagePath = Utils.File.GetRegularPath(Path.Combine(m_package_path, fullNameWithExtension));
        //             string packageDirectoryName = Path.GetDirectoryName(packagePath);
        //             if (!Directory.Exists(packageDirectoryName))
        //             {
        //                 Directory.CreateDirectory(packageDirectoryName);
        //             }
        //
        //             File.WriteAllBytes(packagePath, bytes);
        //         }
        //         else
        //         {
        //             var fs = CreateFileSystem(resourceData, m_package_path);
        //             
        //             var result = fs.WriteFile(fullNameWithExtension, bytes);
        //             fs.Shutdown();
        //             
        //             if (result == false)
        //             {
        //                 return false;
        //             }
        //         }
        //     }
        //
        //     if (OutputPackedSelected && resourceData.Packed)
        //     {
        //         if (string.IsNullOrEmpty(fileSystem))
        //         {
        //             string packedPath = Utils.File.GetRegularPath(Path.Combine(m_packed_path, fullNameWithExtension));
        //             string packedDirectoryName = Path.GetDirectoryName(packedPath);
        //             if (!Directory.Exists(packedDirectoryName))
        //             {
        //                 Directory.CreateDirectory(packedDirectoryName);
        //             }
        //
        //             File.WriteAllBytes(packedPath, bytes);
        //         }
        //         else
        //         {
        //             var fs = CreateFileSystem(resourceData, m_packed_path);
        //             var result = fs.WriteFile(fullNameWithExtension, bytes);
        //             fs.Shutdown();
        //             
        //             if (result == false)
        //             {
        //                 return false;
        //             }
        //         }
        //     }
        //
        //     if (OutputFullSelected)
        //     {
        //         string fullNameWithCrc32AndExtension = variant != null ? Utils.Text.Format("{0}.{1}.{2:x8}.{3}", name, variant, hashCode, DefaultExtension) : Utils.Text.Format("{0}.{1:x8}.{2}", name, hashCode, DefaultExtension);
        //         string fullPath = Utils.File.GetRegularPath(Path.Combine(m_full_path, fullNameWithCrc32AndExtension));
        //         string fullDirectoryName = Path.GetDirectoryName(fullPath);
        //         if (!Directory.Exists(fullDirectoryName))
        //         {
        //             Directory.CreateDirectory(fullDirectoryName);
        //         }
        //
        //         if (zip)
        //         {
        //             byte[] zipBytes = Utils.Zip.Compress(bytes);
        //             zipLength = zipBytes.Length;
        //             zipHashCode = Utils.Verifier.GetCrc32(zipBytes);
        //             File.WriteAllBytes(fullPath, zipBytes);
        //         }
        //         else
        //         {
        //             File.WriteAllBytes(fullPath, bytes);
        //         }
        //     }
        //
        //     resourceData.AddCode(platform, length, hashCode, zipLength, zipHashCode);
        //     return true;
        // }
        public void WriteVersionTxt()
        {
            var filePath = Application.streamingAssetsPath + "/dat/Version.txt";
            if (!File.Exists(filePath))
            {
                Debug.LogError("没有版本信息文件 先打包资源生成版本信息文件");
                return;
            }
            // var str = File.ReadAllText(filePath);
            // var json = LitJson.JsonMapper.ToObject(str);
            // json["SdkChannel"] = SdkChannel.ToString();
            // str = LitJson.JsonMapper.ToJson(json);
            // File.WriteAllText(filePath, str);
            //Debug.Log($"{platform}Version.txt is Generated");
        }

        public BuildAssetBundleOptions GetBuildAssetBundleOptions()
        {
            BuildAssetBundleOptions buildOptions = BuildAssetBundleOptions.None;

            if (UncompressedAssetBundleSelected)
            {
                buildOptions |= BuildAssetBundleOptions.UncompressedAssetBundle;
            }

            if (DisableWriteTypeTreeSelected)
            {
                buildOptions |= BuildAssetBundleOptions.DisableWriteTypeTree;
            }

            if (DeterministicAssetBundleSelected)
            {
                buildOptions |= BuildAssetBundleOptions.DeterministicAssetBundle;
            }

            if (ForceRebuildAssetBundleSelected)
            {
                buildOptions |= BuildAssetBundleOptions.ForceRebuildAssetBundle;
            }

            if (IgnoreTypeTreeChangesSelected)
            {
                buildOptions |= BuildAssetBundleOptions.IgnoreTypeTreeChanges;
            }

            if (AppendHashToAssetBundleNameSelected)
            {
                buildOptions |= BuildAssetBundleOptions.AppendHashToAssetBundleName;
            }

            if (ChunkBasedCompressionSelected)
            {
                buildOptions |= BuildAssetBundleOptions.ChunkBasedCompression;
            }

            return buildOptions;
        }

        private bool PrepareBuildData(out AssetBundleBuild[] assetBundleBuildDatas,
            out ResourceData[] assetBundleResourceDatas)
        {
            assetBundleBuildDatas = null;
            assetBundleResourceDatas = null;
            m_ResourceDatas.Clear();

            Resource[] resources = m_ResourceCollection.GetResources();
            foreach (Resource resource in resources)
            {
                //UnityEngine.Debug.Log($"resourceName = {resource.FullName}, OnlySingleBuild = {OnlySingleBuildSelected.ToString()}, resource.SingleBuild = {resource.SingleBuild.ToString()}");
                if (OnlySingleBuildSelected && resource.SingleBuild == false)
                {
                    continue;
                }

                m_ResourceDatas.Add(resource.FullName,
                    new ResourceData(resource.Name, resource.Variant, resource.FileSystem, resource.LoadType,
                        resource.Packed, resource.GetResourceGroups()));
            }

            Asset[] assets = m_ResourceCollection.GetAssets();
            foreach (Asset asset in assets)
            {
                if (OnlySingleBuildSelected && !asset.Resource.SingleBuild)
                {
                    continue;
                }

                string assetName = asset.Path;
                if (string.IsNullOrEmpty(assetName))
                {
                    m_BuildReport.LogError("Can not find asset by guid '{0}'.", asset.Guid);
                    return false;
                }


                string assetFileFullName =
                    Application.dataPath.Substring(0, Application.dataPath.Length - AssetsStringLength) + assetName;
                if (!File.Exists(assetFileFullName))
                {
                    m_BuildReport.LogError("Can not find asset '{0}' resource '{1}'.", assetFileFullName,
                        asset.Resource.FullName);
                    return false;
                }

                byte[] assetBytes = File.ReadAllBytes(assetFileFullName);
                // int assetHashCode = Utils.Verifier.GetCrc32(assetBytes);
                int assetHashCode = 0;

                List<string> dependencyAssetNames = new List<string>();
                DependencyData dependencyData = m_ResourceAnalyzerController.GetDependencyData(assetName);
                Asset[] dependencyAssets = dependencyData.GetDependencyAssets();
                foreach (Asset dependencyAsset in dependencyAssets)
                {
                    dependencyAssetNames.Add(dependencyAsset.Path);
                }

                dependencyAssetNames.Sort();

                m_ResourceDatas[asset.Resource.FullName].AddAssetData(asset.Guid, assetName, assetBytes.Length,
                    assetHashCode, dependencyAssetNames.ToArray());
            }

            List<AssetBundleBuild> assetBundleBuildDataList = new List<AssetBundleBuild>();
            List<ResourceData> assetBundleResourceDataList = new List<ResourceData>();
            foreach (ResourceData resourceData in m_ResourceDatas.Values)
            {
                if (resourceData.AssetCount <= 0)
                {
                    m_BuildReport.LogError("Resource '{0}' has no asset.",
                        GetResourceFullName(resourceData.Name, resourceData.Variant));
                    return false;
                }

                assetBundleResourceDataList.Add(resourceData);

                AssetBundleBuild build = new AssetBundleBuild();
                build.assetBundleName = resourceData.Name;
                build.assetBundleVariant = resourceData.Variant;
                build.assetNames = resourceData.GetAssetNames();
                assetBundleBuildDataList.Add(build);
            }

            assetBundleBuildDatas = assetBundleBuildDataList.ToArray();
            assetBundleResourceDatas = assetBundleResourceDataList.ToArray();
            return true;
        }

        private static string GetResourceFullName(string name, string variant)
        {
            return !string.IsNullOrEmpty(variant) ? Utils.Text.Format("{0}.{1}", name, variant) : name;
        }

        private static BuildTarget GetBuildTarget(Platform platform)
        {
            switch (platform)
            {
                case Platform.Windows:
                    return BuildTarget.StandaloneWindows;

                case Platform.Windows64:
                    return BuildTarget.StandaloneWindows64;

                case Platform.MacOS:
#if UNITY_2017_3_OR_NEWER
                    return BuildTarget.StandaloneOSX;
#else
                    return BuildTarget.StandaloneOSXUniversal;
#endif
                case Platform.Linux:
                    return BuildTarget.StandaloneLinux64;

                case Platform.IOS:
                    return BuildTarget.iOS;

                case Platform.Android:
                    return BuildTarget.Android;

                case Platform.WindowsStore:
                    return BuildTarget.WSAPlayer;

                case Platform.WebGL:
                    return BuildTarget.WebGL;

                default:
                    throw new Exception("Platform is invalid.");
            }
        }

        private static string GetExtension(ResourceData data)
        {
            if (data.IsLoadFromBinary)
            {
                string assetName = data.GetAssetNames()[0];
                int position = assetName.LastIndexOf('.');
                if (position >= 0)
                {
                    return assetName.Substring(position + 1);
                }
            }

            return DefaultExtension;
        }
    }
}